x86/p2m: Add p2m_change_type_range() operation
authorTim Deegan <Tim.Deegan@citrix.com>
Thu, 30 Jun 2011 09:26:54 +0000 (10:26 +0100)
committerTim Deegan <Tim.Deegan@citrix.com>
Thu, 30 Jun 2011 09:26:54 +0000 (10:26 +0100)
that defers the nested-p2m flush until the entire batch has been updated.
Use it in the HAP log-dirty operations for tracking VRAM changes.
This should avoid a lot of unpleasant IPI storms as the log-dirty code
on one CPU repeatedly shoots down the nested p2m of another CPU.

Signed-off-by: Tim Deegan <Tim.Deegan@citrix.com>
xen/arch/x86/mm/hap/hap.c
xen/arch/x86/mm/p2m.c
xen/include/asm-x86/p2m.h

index 92cf2690abb40f8a7bf5dc5a6cc1c2534e0c4a17..f90511ef04d22d263f698e15f1a3aab9f9bc79fd 100644 (file)
@@ -58,7 +58,6 @@
 
 static int hap_enable_vram_tracking(struct domain *d)
 {
-    int i;
     struct sh_dirty_vram *dirty_vram = d->arch.hvm_domain.dirty_vram;
 
     if ( !dirty_vram )
@@ -70,8 +69,8 @@ static int hap_enable_vram_tracking(struct domain *d)
     paging_unlock(d);
 
     /* set l1e entries of P2M table to be read-only. */
-    for (i = dirty_vram->begin_pfn; i < dirty_vram->end_pfn; i++)
-        p2m_change_type(d, i, p2m_ram_rw, p2m_ram_logdirty);
+    p2m_change_type_range(d, dirty_vram->begin_pfn, dirty_vram->end_pfn, 
+                          p2m_ram_rw, p2m_ram_logdirty);
 
     flush_tlb_mask(d->domain_dirty_cpumask);
     return 0;
@@ -79,7 +78,6 @@ static int hap_enable_vram_tracking(struct domain *d)
 
 static int hap_disable_vram_tracking(struct domain *d)
 {
-    int i;
     struct sh_dirty_vram *dirty_vram = d->arch.hvm_domain.dirty_vram;
 
     if ( !dirty_vram )
@@ -90,8 +88,8 @@ static int hap_disable_vram_tracking(struct domain *d)
     paging_unlock(d);
 
     /* set l1e entries of P2M table with normal mode */
-    for (i = dirty_vram->begin_pfn; i < dirty_vram->end_pfn; i++)
-        p2m_change_type(d, i, p2m_ram_logdirty, p2m_ram_rw);
+    p2m_change_type_range(d, dirty_vram->begin_pfn, dirty_vram->end_pfn, 
+                          p2m_ram_logdirty, p2m_ram_rw);
 
     flush_tlb_mask(d->domain_dirty_cpumask);
     return 0;
@@ -99,15 +97,14 @@ static int hap_disable_vram_tracking(struct domain *d)
 
 static void hap_clean_vram_tracking(struct domain *d)
 {
-    int i;
     struct sh_dirty_vram *dirty_vram = d->arch.hvm_domain.dirty_vram;
 
     if ( !dirty_vram )
         return;
 
     /* set l1e entries of P2M table to be read-only. */
-    for (i = dirty_vram->begin_pfn; i < dirty_vram->end_pfn; i++)
-        p2m_change_type(d, i, p2m_ram_rw, p2m_ram_logdirty);
+    p2m_change_type_range(d, dirty_vram->begin_pfn, dirty_vram->end_pfn, 
+                          p2m_ram_rw, p2m_ram_logdirty);
 
     flush_tlb_mask(d->domain_dirty_cpumask);
 }
@@ -863,7 +860,8 @@ hap_write_p2m_entry(struct vcpu *v, unsigned long gfn, l1_pgentry_t *p,
     paging_lock(d);
     old_flags = l1e_get_flags(*p);
 
-    if ( nestedhvm_enabled(d) && (old_flags & _PAGE_PRESENT) ) {
+    if ( nestedhvm_enabled(d) && (old_flags & _PAGE_PRESENT) 
+         && !p2m_get_hostp2m(d)->defer_nested_flush ) {
         /* We are replacing a valid entry so we need to flush nested p2ms,
          * unless the only change is an increase in access rights. */
         mfn_t omfn = _mfn(l1e_get_pfn(*p));
index 9592b48030d308bac959037f7b69e653b9f53413..a8f18ac484742b89a4fd404b22a6254360a6d197 100644 (file)
@@ -537,6 +537,37 @@ p2m_type_t p2m_change_type(struct domain *d, unsigned long gfn,
     return pt;
 }
 
+/* Modify the p2m type of a range of gfns from ot to nt.
+ * Resets the access permissions. */
+void p2m_change_type_range(struct domain *d, 
+                           unsigned long start, unsigned long end,
+                           p2m_type_t ot, p2m_type_t nt)
+{
+    p2m_type_t pt;
+    unsigned long gfn;
+    mfn_t mfn;
+    struct p2m_domain *p2m = p2m_get_hostp2m(d);
+
+    BUG_ON(p2m_is_grant(ot) || p2m_is_grant(nt));
+
+    p2m_lock(p2m);
+    p2m->defer_nested_flush = 1;
+
+    for ( gfn = start; gfn < end; gfn++ )
+    {
+        mfn = gfn_to_mfn_query(d, gfn, &pt);
+        if ( pt == ot )
+            set_p2m_entry(p2m, gfn, mfn, 0, nt, p2m->default_access);
+    }
+
+    p2m->defer_nested_flush = 0;
+    if ( nestedhvm_enabled(d) )
+        p2m_flush_nestedp2m(d);
+    p2m_unlock(p2m);
+}
+
+
+
 int
 set_mmio_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn)
 {
index ff3f1a00546778dce872dfd36a550c223e56e6ba..c39ebd2c55a91f6cb0f71a2bca1a06147404de8c 100644 (file)
@@ -209,6 +209,12 @@ struct p2m_domain {
 #define CR3_EADDR     (~0ULL)
     uint64_t           cr3;
 
+    /* Host p2m: when this flag is set, don't flush all the nested-p2m 
+     * tables on every host-p2m change.  The setter of this flag 
+     * is responsible for performing the full flush before releasing the
+     * host p2m's lock. */
+    int                defer_nested_flush;
+
     /* Pages used to construct the p2m */
     struct page_list_head pages;
 
@@ -408,6 +414,11 @@ int guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn,
 void p2m_change_entry_type_global(struct domain *d, 
                                   p2m_type_t ot, p2m_type_t nt);
 
+/* Change types across a range of p2m entries (start ... end-1) */
+void p2m_change_type_range(struct domain *d, 
+                           unsigned long start, unsigned long end,
+                           p2m_type_t ot, p2m_type_t nt);
+
 /* Compare-exchange the type of a single p2m entry */
 p2m_type_t p2m_change_type(struct domain *d, unsigned long gfn,
                            p2m_type_t ot, p2m_type_t nt);